package br.indt.framework.ui.common;

import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;
import javax.microedition.lcdui.TextBox;
import javax.microedition.midlet.MIDlet;

import com.nokia.mid.ui.FullCanvas;

/**
 * This class is the main bound between the frame work and 
 * the LCDUI base. This class is charged to detect primary
 * gestures and display main containers like Screen components. 
 * 
 * @author Nokia Institute of Technology
 *
 */
public class Display extends Canvas implements CommandListener,ButtonActionListener {
	
	public static final byte UNKNOWN = -1;
	public static final byte TYPE = 0;
	public static final byte TOUCH_TYPE = 1;
	public static final byte FULL_TOUCH = 2;
	public static final byte TYPE_LANDSCAPE = 3;
	public static final byte NEXT_GENERATION = 4;	

	public static final int DOWN = 1;
	public static final int UP = 2;
	public static final int RIGHT = 3;
	public static final int LEFT = 4;

	public static final int TITLE_BAR_HEIGHT = 45;
	public static final int MENU_BAR_HEIGHT = 33;
	public static final int MENU_BAR_HEIGHT_NG = 10;
	public static final int MENU_BAR_HEIGHT_TOUCH = 49;
	private Vector displayables = null;
	private static Display me = null;
	private Displayable currentDisplayable = null;
	private Displayable lastDisplayable = null;	
	private Displayable loadingDisplayable = null;
	private MIDlet midlet = null;
	private int[] rgbData = null;
	private BusyAnimation busyAnimation = null;
	private boolean showAlert = false;
	private Alert alert = null;

	//private boolean hideAction = false;

	private Object frameAnimator = null;

	private Vector textBoxUpdateListeners = null;

	private TextBoxUpdateListener currentTextBoxUpdateListener = null; // set current TextBoxUpdateListener
	
	private FocusListener focusListener = null;

	private ScreenListener screenListener = null;

	private boolean controllerListener = false;

	public static int keyCodePressed = 0;

	private Display(MIDlet midlet) {
		setFullScreenMode(true);		
		
		this.midlet = midlet;
		me = this;
		displayables = new Vector();
		textBoxUpdateListeners = new Vector();
		controllerListener = false;
		try {

			
			if (isTouchAndType()) {
				new CirclesGestureListener(this);

				CirclesFrameAnimatorListener frameAnimatorListener = new CirclesFrameAnimatorListener(this);
				frameAnimator = frameAnimatorListener.getFrameAnimator();
			}

			rgbData = new int[getWidth() * getHeight()];
			for(int i=0; i<rgbData.length;i+=5) {
				rgbData[i] = rgbData[i+1] = rgbData[i+2] = rgbData[i+3] = rgbData[i+4] = 0x90000000;
			}

			alert = new Alert(getWidth(), getHeight(), this);
			alert.setMargin(15, 0, 15, 15);

			busyAnimation = new BusyAnimation(getWidth(), getHeight(), this);
			busyAnimation.setMargin(15, 0, 15, 15);

		} catch (Exception e) {
			
		}
	}
	
	public static boolean isTouchAndType() {
		return (System.getProperty("com.nokia.mid.ui.frameanimator.fps") != null);
	}	
	
	public static byte getDeviceType() {
		return FULL_TOUCH;
	}	
	
	
	public Displayable getLoadingDisplayable() {
		return loadingDisplayable;
	}
	
	protected void keyRepeated(int keyCode) {
		keyCodePressed = 0;
		switch(getGameAction(keyCode)) {
			case FullCanvas.UP:
				keyCodePressed = UP;
				if ((keyCode != 50 && keyCode != 116) && (!showAlert && !currentDisplayable.isBusy())) {
					
					if(focusListener == null){
						commandEvent(UP);			
					}else{
						if(!controllerListener){
							commandEvent(UP);
						}else{
							focusListener.focusLost(currentDisplayable.getCurrentItemObject());	
						}
					}
				}
				break;
	
			case FullCanvas.DOWN:
				keyCodePressed = DOWN;
				if ((keyCode != 56 && keyCode != 98) && (!showAlert && !currentDisplayable.isBusy())) {
									
					if(focusListener == null){
						commandEvent(DOWN);					
					}else{
						if (!controllerListener){
							commandEvent(DOWN);
						}else{
							focusListener.focusGained(currentDisplayable.getCurrentItemObject());
						}
					}
				}
				break;
		}
	}

	protected void keyPressed(int keyCode) {
		keyCodePressed = 0;
		switch(getGameAction(keyCode)) {
		case FullCanvas.FIRE:
			if (loadingDisplayable != null) {
				break;
			}else if (showAlert) {
				alert.getButton().pressFire();

			} else if(currentDisplayable.isBusy()) {
				busyAnimation.getButton().pressFire();

			} else if ((keyCode != 53) && (!showAlert && !currentDisplayable.isBusy())) {
				if(currentDisplayable.getName().equals("cameraForm")){
					if ((currentDisplayable.getMenuBar().getMSKItem() != null) && (!showAlert && !currentDisplayable.isBusy()))	{
						currentDisplayable.getMenuBar().click(-1, -1);
					}

				} else {
					if(currentDisplayable.getName().equals("welcome_screen")){
						if ((currentDisplayable.getMenuBar().getMSKItem() != null) && (!showAlert && !currentDisplayable.isBusy()))	{
							currentDisplayable.getMenuBar().click(-1, -1);
						}

					} else if ((Widget)currentDisplayable.getCurrentItemObject() != null) {
						((Widget)currentDisplayable.getCurrentItemObject()).pressFire();
					}
				}
			}
			break;

		case FullCanvas.UP:
			keyCodePressed = UP;
			if ((keyCode != 50 && keyCode != 116) && (!showAlert && !currentDisplayable.isBusy())) {
				
				if(focusListener == null){
					commandEvent(UP);			
				}else{
					if(!controllerListener){
						commandEvent(UP);
					}else{
						focusListener.focusLost(currentDisplayable.getCurrentItemObject());	
					}
				}
			}
			break;

		case FullCanvas.DOWN:
			keyCodePressed = DOWN;
			if ((keyCode != 56 && keyCode != 98) && (!showAlert && !currentDisplayable.isBusy())) {
								
				if(focusListener == null){
					commandEvent(DOWN);					
				}else{
					if (!controllerListener){
						commandEvent(DOWN);
					}else{
						focusListener.focusGained(currentDisplayable.getCurrentItemObject());
					}
				}
			}
			break;

		case FullCanvas.RIGHT:
			keyCodePressed = RIGHT;

			commandEvent(RIGHT);
			break;

		case FullCanvas.LEFT:
			keyCodePressed = LEFT;
			
			commandEvent(LEFT);
			break;
		}

		switch(keyCode) {
		case FullCanvas.KEY_SOFTKEY1:
			if ((!showAlert && !currentDisplayable.isBusy())) {
				if (currentDisplayable.getMenuBar() != null){
					currentDisplayable.getMenuBar().click(0, 0);
				}
				
			}
			break;

		case FullCanvas.KEY_SOFTKEY2:
		    if ((currentDisplayable.getMenuBar()!=null)&&(currentDisplayable.getMenuBar().getRSKItem() != null) && (!showAlert && !currentDisplayable.isBusy())) {
					currentDisplayable.getMenuBar().click(215, 0);
				
			}
		    break;

		case FullCanvas.KEY_SOFTKEY3:
			
//			if (UIManager.isPopUpConfirmScreenVisible)
//			{
//				if (((currentDisplayable.getMenuBar()!=null)&&(currentDisplayable.getMenuBar().getMSKItem() != null) && (!showAlert && !currentDisplayable.isBusy())))
//				{
//					currentDisplayable.getMenuBar().click(-1, -1);
//				}
//			}
			break;
		}
	}

	public static void init(MIDlet midlet) {
		 if(me == null) {
			 me = new Display(midlet);
			 javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(me);
		 }
	}
	
	public void callSerially(Runnable runnable) {
		javax.microedition.lcdui.Display.getDisplay(midlet).callSerially(runnable);
	}

	public static Display getInstance() {
		if(me == null) {
			throw new RuntimeException("Display not initialized");
		}
		return me;
	}

	public MIDlet getMidlet() {
		return midlet;
	}

	public void refresh(Displayable displayable, boolean serviceRepaints) {
		if(currentDisplayable == displayable) {
			
//			//#ifdef NG
//			if (currentDisplayable instanceof Form) {
//				TitleBar tb = ((Form)currentDisplayable).getTitleBar();
//				if (tb != null) {
//					int color = ((Form)currentDisplayable).getTitleBar().getBackgroundColor();
//					color |= 0xFF000000;
//					setMenuIndicatorRGBData(color);
//				}
//			}			
//			//#endif
			
			repaint();

			if(serviceRepaints) {
				serviceRepaints();
			}
		}
	}

	public void refresh(boolean serviceRepaints) {
		refresh(currentDisplayable, serviceRepaints);
	}

	public void paint(Graphics g) {
		try {
			if(loadingDisplayable != null){
				loadingDisplayable.paint(g, 0, 0, getWidth(), getHeight());
			}else{
			
				if (currentDisplayable != null) {
					
					//currentDisplayable.paint(g, 0, 0, getWidth(), getHeight());
					currentDisplayable.paint(g, 0, 0, 240, 400);
	
					if (currentDisplayable.isShowPopupMenu() && currentDisplayable.getPopupMenu() != null) {
						g.setClip(0, 0, getWidth(), getHeight());
						int menubar_size = MENU_BAR_HEIGHT_NG;
						if(getDeviceType() != NEXT_GENERATION){
							menubar_size = isTouchAndType() ? MENU_BAR_HEIGHT_TOUCH : MENU_BAR_HEIGHT;	
						}
						
	
						g.drawRGB(rgbData, 0, getWidth(), 0, 0, getWidth(), getHeight() - menubar_size, true);
						currentDisplayable.getPopupMenu().paint(g, 0, 0, getWidth(), getHeight());
	
					} else if (showAlert) {
						g.setClip(0, 0, getWidth(), getHeight());
						g.drawRGB(rgbData, 0, getWidth(), 0, 0, getWidth(), getHeight(), true);
						alert.paint(g, 0, 0, getWidth(), getHeight());
	
					} else if (currentDisplayable.isBusy()) {
						g.setClip(0, 0, getWidth(), getHeight());
						g.drawRGB(rgbData, 0, getWidth(), 0, 0, getWidth(), getHeight(), true);
						busyAnimation.paint(g, 0, 0, getWidth(), getHeight());
					} 
					
				}
			}
		} catch (Exception e) {
			//empty
		}
	}
	
	public Displayable getCurrentDisplayable() {
		Displayable result = null;

		if (currentDisplayable != null) {
			if (showAlert) {
				result = alert;

			} else if (currentDisplayable.isBusy()) {
				result = busyAnimation;
				
			} else {
				result = currentDisplayable;
			}

		} else {
			result = busyAnimation;
		}

		return result;
	}

	public Displayable getLastDisplayable() {
		return lastDisplayable;
	}
	
	public void setCurrent(Displayable displayable) {
		if (currentDisplayable != null) {
			currentDisplayable.releaseTextEditor();
		}
		removeCommands(currentDisplayable);
		currentDisplayable = null;		
		lastDisplayable = null;
		displayables.removeAllElements();
		push(displayable);
	}

	public void push(Displayable displayable) {
		if (currentDisplayable != null) {
			lastDisplayable = currentDisplayable;
		}

		if (currentDisplayable != displayable) {
			displayables.addElement(displayable);
			currentDisplayable = displayable;

			try {
				//UIManager.getInstance().setOptionMenuScreen(null);

			} catch (Exception e) {
				
			}
			
			//removeCommands(lastDisplayable);
			
			addCommands(currentDisplayable);			
			
		}

		refresh(true);
	}

	private void addCommands(Displayable displayable) {
		if (displayable != null && displayable.hasCommands()) {
			
			Enumeration currentCommands = displayable.getCommandEnum();
			
			while (currentCommands.hasMoreElements()) {
				addCommand((Command)currentCommands.nextElement());
			}
			
			setCommandListener(displayable.getCommandListener());
		}
	}

	public void removeCommands(Displayable displayable) {
		if (displayable != null && displayable.hasCommands()) {
			
			Enumeration lastCommands = displayable.getCommandEnum();
			
			while (lastCommands.hasMoreElements()) {
				removeCommand((Command)lastCommands.nextElement());
			}
			
			setCommandListener(null);
		}
	}

	public void pushLoading(Displayable displayable)
	{
		loadingDisplayable = displayable;
		
		removeCommands(currentDisplayable);
		addCommands(loadingDisplayable);
		
		refresh(true);
	}
	
	public void popLoading()
	{
		removeCommands(loadingDisplayable);
		loadingDisplayable = null;
		addCommands(currentDisplayable);

		refresh(true);
	}
	
	public void pushAlert(javax.microedition.lcdui.Alert alert) {
		javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(alert, this);
	}
	
	public void popAlert() {
		javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(this);
	}

	public void pop() {
		
		removeCommands(currentDisplayable);
		currentDisplayable.releaseTextEditor();
		
		if(displayables.size() > 0) {
			displayables.removeElementAt(displayables.size() - 1);
			currentDisplayable = (Displayable) displayables.elementAt(displayables.size() - 1);
		}

		if(displayables.size() > 1) {
			lastDisplayable = (Displayable) displayables.elementAt(displayables.size() - 2);
		}

		addCommands(currentDisplayable);
		
		refresh(true);
	}

	protected void pointerPressed(int x, int y) {
		if(frameAnimator != null) {
			if (isTouchAndType()) {
				((com.nokia.mid.ui.frameanimator.FrameAnimator)frameAnimator).stop();
			}
		}

		if(getCurrentDisplayable() != null) {
			
			//if(screenListener != null && getCurrentDisplayable() instanceof Form ){
			//	if(((Form)getCurrentDisplayable()).getName().equals("OptionMenuFullTouch")){
			//		getCurrentDisplayable().pointerPressed(x, y);
			//		refresh(true);
			//	}

			//} else {
				getCurrentDisplayable().pointerPressed(x, y);
				refresh(true);
			//}
			
		}
	}

	protected void pointerReleased(int x, int y) {
		
		if(getCurrentDisplayable() != null) {
			if(((Form)getCurrentDisplayable()).getName().equals("OptionMenuFullTouch")){
				screenListener.screenClicked(x, y);	
				refresh(true);
			}else{
				getCurrentDisplayable().pointerReleased(x, y);
				refresh(true);	
			}
			
		}
	}

	public int getScrollableHeight() {
		int menubar_size = MENU_BAR_HEIGHT_NG;
		menubar_size = isTouchAndType() ? MENU_BAR_HEIGHT_TOUCH : MENU_BAR_HEIGHT;
		
		return getHeight() - menubar_size - TITLE_BAR_HEIGHT;
	}
	
	public int getContentHeight() {
		if(currentDisplayable != null) {
			return currentDisplayable.getContentHeight();
		}
		return 0;
	}
	
	public Object getFrameAnimator() {
		return frameAnimator;
	}

	public void pop(Displayable displayable) {
		if(currentDisplayable == displayable) {
			pop();
		}
	}
	
	
	public void showTextBox(String title, String text, int maxSize, int constraints, TextBoxUpdateListener textBoxUpdateListener) {
		TextBox textBox = new TextBox(title, text, maxSize, constraints);
		textBox.addCommand(new Command("OK", Command.OK, 0));
		textBox.setCommandListener(this);
		javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(textBox);
		addTextBoxUpdateListener(textBoxUpdateListener);
		currentTextBoxUpdateListener = textBoxUpdateListener;
	}

	public void commandAction(Command c, javax.microedition.lcdui.Displayable d) {

		if (d instanceof TextBox) {
			if (textBoxUpdateListeners != null) {

				for (int i = 0; i < textBoxUpdateListeners.size(); i++) {
					TextBoxUpdateListener value = (TextBoxUpdateListener) textBoxUpdateListeners
							.elementAt(i);
					
					if(value == currentTextBoxUpdateListener){
						value.textUpdated(((TextBox) d).getString());
					}
				}
			}

			if (currentDisplayable != null) {
				javax.microedition.lcdui.Display.getDisplay(midlet).setCurrent(this);
			}
		} 
		
	}

	public void showAlert(String text, Image image) {
		showAlert = true;
		alert.setText(text, image);
		refresh(true);
	}

	public void buttonClicked(Button button) {
		try {
			if(showAlert) {
				showAlert = false;

			} else if(currentDisplayable.isBusy()) {
//				currentDisplayable.busyOperationCanceled();
			}
			refresh(true);

		} catch (Exception e) {
			
		}
		
	}

	public void commandEvent(int event) {
		switch(event) {
			case UP:
				currentDisplayable.upItem();
				break;

			case DOWN:
				currentDisplayable.downItem();
				break;
			
			case RIGHT:
				currentDisplayable.rightItem();
				break;
				
			case LEFT:
				currentDisplayable.leftItem();
				break;
				
		}

		refresh(true);
	}
	
	public void addTextBoxUpdateListener(TextBoxUpdateListener listener){
		
		boolean exists = false;
		
		for (int i = 0; i < textBoxUpdateListeners.size(); i++) {
			
			TextBoxUpdateListener value = (TextBoxUpdateListener) textBoxUpdateListeners.elementAt(i);
			if (listener == value) {
				exists = true;
				break;
			}
		}
		
		if (!exists) {
			textBoxUpdateListeners.addElement(listener);
		}

	}
	
	public FocusListener getFocusListener() {
		return focusListener;
	}

	public void setFocusListener(FocusListener focusListener) {
		this.focusListener = focusListener;
	}
	
	public boolean isControllerListener() {
		return controllerListener;
	}

	public void setControllerListener(boolean controllerListener) {
		this.controllerListener = controllerListener;
	}
	public void popWithoutRefresh() {
		if(displayables.size() > 0) {
			displayables.removeElementAt(displayables.size() - 1);
			currentDisplayable = (Displayable) displayables.elementAt(displayables.size() - 1);
		}
	}
	
	public void setScreenListener(ScreenListener screenListener) {
		this.screenListener = screenListener;
	}
	
	public ScreenListener getScreenListener() {
		return screenListener;
	}
	

	public boolean empilhado(Displayable displayable){
		for(int i = 0; i < displayables.size(); i++){
			if(displayable == (Displayable)displayables.elementAt(i))
				return true;
		}
		return false;
	}
	
	public void desempilha(Displayable displayable){
		if (empilhado(displayable)){
			for(int i = displayables.size() -1; i >= 0; i--){
				if(displayable == (Displayable)displayables.elementAt(i)){
					return;
				}
				pop();
			}
		}
	}
	
	public void desempilha() {

		if (displayables != null && displayables.size() > 0) {
			for (int i = displayables.size() - 1; i >= 0; i--) {
				displayables.removeElementAt(i);
			}
			currentDisplayable = null;
			lastDisplayable = null;
			refresh(true);
		}
	}


	public Vector getDisplayables() {
		return displayables;
	}
	
	
//	public void setHideAction(boolean hideAction) { temporariamente comentado para estudar uma solucao melhor
//		this.hideAction = hideAction;
//	}
//	
//	public boolean isHideAction() {
//		return hideAction;
//	}
}
